import 'dart:math';

import 'package:flutter/material.dart';

class CurvesPaint extends StatelessWidget {
  const CurvesPaint({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      painter: WavePainter(startAngle: 0.0),
    );
  }
}

class WaveAnimationWidget extends StatefulWidget {
  WaveAnimationWidget({Key? key}) : super(key: key);

  @override
  State<WaveAnimationWidget> createState() => _WaveAnimationWidgetState();
}

class _WaveAnimationWidgetState extends State<WaveAnimationWidget>
    with SingleTickerProviderStateMixin {
  late Animation<double> _animation;
  late AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller =
        AnimationController(vsync: this, duration: Duration(seconds: 8));
    _animation = Tween<double>(begin: 1.0, end: 0.0).animate(_controller);
    _controller.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        _controller.forward(from: 0.0);
      }
    });
    _controller.addListener(() {
      setState(() {});
    });
    _controller.forward();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Color(0xFFF5F5F5),
      appBar: AppBar(
        title: Text('曲线绘制'),
        brightness: Brightness.dark,
        backgroundColor: Colors.blue[600],
        shadowColor: Colors.transparent,
      ),
      body: ListView(
        children: [
          CustomPaint(
            foregroundPainter: WavePainter(
              startAngle: _animation.value,
            ),
            child: Container(
              alignment: Alignment.topCenter,
              width: MediaQuery.of(context).size.width,
              height: 200,
              //color: Colors.blue[200],
              decoration: BoxDecoration(
                gradient: LinearGradient(
                  colors: [
                    Colors.blue[600]!,
                    Colors.blue[400]!,
                    Colors.blue[200]!,
                  ],
                  begin: Alignment.topCenter,
                  end: Alignment.bottomCenter,
                ),
              ),
            ),
          ),
          CustomPaint(
            painter: LogCurvePainter(),
            child: Container(
              alignment: Alignment.topCenter,
              width: MediaQuery.of(context).size.width,
              height: 300,
            ),
          ),
          CustomPaint(
            foregroundPainter: LineChartPainter(
              points: [
                Point(10, 100),
                Point(50, 80),
                Point(90, 150),
                Point(130, 160),
                Point(170, 100),
                Point(210, 100),
                Point(250, 190),
                Point(290, 150),
                Point(330, 160),
                Point(370, 100),
                Point(410, 160),
              ],
            ),
            painter: AxisPainter(
              horizontalStartPoint: Point(0.0, 150.0),
              horizontalEndPoint: Point(MediaQuery.of(context).size.width, 150),
              verticalStartPoint:
                  Point(MediaQuery.of(context).size.width / 2, 300),
              verticalEndPoint: Point(MediaQuery.of(context).size.width / 2, 0),
            ),
            child: Container(
              alignment: Alignment.topCenter,
              width: MediaQuery.of(context).size.width,
              height: 300,
            ),
          ),
        ],
      ),
    );
  }
}

class WavePainter extends CustomPainter {
  final double startAngle;
  WavePainter({Key? key, required this.startAngle}) : super();
  final waveHeight = 40.0;
  @override
  void paint(Canvas canvas, Size size) {
    var center = Size(size.width / 2, waveHeight * 2);
    var paint1 = Paint()..color = Color(0xFF20B0FE); //2080E5
    paint1.strokeWidth = 1.0;
    paint1.style = PaintingStyle.stroke;

    var paint2 = Paint()..color = Color(0x8020C0E5); //20E560
    paint2.strokeWidth = 1.0;
    paint2.style = PaintingStyle.stroke;

    Path path1 = Path();
    path1.moveTo(0, center.height);
    Path path2 = Path();
    path2.moveTo(0, center.height + waveHeight);
    for (double i = 1; i < size.width; i += 1) {
      path1.lineTo(
        i,
        center.height +
            waveHeight * sin(2 * pi * i / size.width + startAngle * pi * 4),
      );
      path2.lineTo(
        i,
        center.height +
            waveHeight * sin(2 * pi * i / size.width + startAngle * 6 * pi),
      );
    }
    canvas.drawPath(path1, paint1);
    canvas.drawPath(path2, paint2);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
}

class LogCurvePainter extends CustomPainter {
  LogCurvePainter({Key? key}) : super();
  @override
  void paint(Canvas canvas, Size size) {
    var center = Size(size.width / 2, size.height / 2);
    var paint = Paint()..color = Color(0xFF2080E5); //2080E5
    paint.strokeWidth = 1.0;
    paint.style = PaintingStyle.stroke;

    Path path = Path();
    path.moveTo(0, center.height);
    for (double i = 1; i <= size.width; i += 1) {
      path.lineTo(
        i - 1,
        center.height - 20.0 * log(i),
      );
    }
    //print(path);
    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return false;
  }
}

class LineChartPainter extends CustomPainter {
  final List<Point<double>> points;
  LineChartPainter({Key? key, required this.points}) : super();
  @override
  void paint(Canvas canvas, Size size) {
    var paint = Paint()..color = Color(0xFF2080E5); //2080E5
    paint.strokeWidth = 2.0;
    paint.style = PaintingStyle.stroke;

    var pointPaint = Paint()..color = Color(0xFF20FF65); //2080E5
    pointPaint.strokeWidth = 1.0;
    pointPaint.style = PaintingStyle.stroke;

    Path path = Path();
    path.moveTo(points[0].x, points[0].y);
    for (var point in points) {
      path.lineTo(point.x, point.y);
      canvas.drawCircle(Offset(point.x, point.y), 4.0, pointPaint);
    }
    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return false;
  }
}

class AxisPainter extends CustomPainter {
  final Point<double> horizontalStartPoint, horizontalEndPoint;
  final Point<double> verticalStartPoint, verticalEndPoint;
  AxisPainter({
    Key? key,
    required this.horizontalStartPoint,
    required this.horizontalEndPoint,
    required this.verticalStartPoint,
    required this.verticalEndPoint,
  }) : super();
  @override
  void paint(Canvas canvas, Size size) {
    var paint = Paint()..color = Color(0xFF909090);
    paint.strokeWidth = 2.0;
    paint.style = PaintingStyle.stroke;

    Path horizontalPath = Path();
    horizontalPath.moveTo(horizontalStartPoint.x, horizontalStartPoint.y);
    horizontalPath.lineTo(horizontalEndPoint.x - 1, horizontalEndPoint.y);
    canvas.drawPath(horizontalPath, paint);

    Path verticalPath = Path();
    verticalPath.moveTo(verticalStartPoint.x, verticalStartPoint.y);
    verticalPath.lineTo(verticalEndPoint.x, verticalEndPoint.y + 1);
    canvas.drawPath(verticalPath, paint);

    paint.style = PaintingStyle.fill;
    paint.strokeWidth = 2.0;
    final double arrowLength = 12.0;
    // 画箭头
    Path horizontalArrow = Path();
    horizontalArrow.moveTo(horizontalEndPoint.x, horizontalEndPoint.y);
    horizontalArrow.lineTo(horizontalEndPoint.x - arrowLength,
        horizontalEndPoint.y - arrowLength / 2);
    horizontalArrow.lineTo(horizontalEndPoint.x - arrowLength,
        horizontalEndPoint.y + arrowLength / 2);
    horizontalArrow.close();
    canvas.drawPath(horizontalArrow, paint);

    // 画箭头
    Path verticalArrow = Path();
    verticalArrow.moveTo(verticalEndPoint.x, verticalEndPoint.y);
    verticalArrow.lineTo(
        verticalEndPoint.x - arrowLength / 2, verticalEndPoint.y + arrowLength);
    verticalArrow.lineTo(
        verticalEndPoint.x + arrowLength / 2, verticalEndPoint.y + arrowLength);
    verticalArrow.close();
    canvas.drawPath(verticalArrow, paint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return false;
  }
}
